<?php

	namespace Aspic\Database;
	use Aspic as A;

	/**
	* Represent Database Table
	*
	* @use SqlUtil
	*
	* @Author Mallory Dessaintes <mdessaintes@gmail.com>
	* @version 1.0
	*/
	abstract class AbstractTable implements A\Model\CrudModelInterface {
		
		const ROW_CLASS = 'Aspic\Model\DynamicRow';

		protected $_dataModel;

		protected $_sqlUtil;
		
		protected $_prepareForPersistanceFunc;

		protected function __construct(A\Model\TableDataModel $dataModel, QueryExecuterInterface $db) {
			$this->setDataModel($dataModel);
			$this->init($db);
		}
		
		protected function init(QueryExecuterInterface $db) {
			$this->_db = $db;
			
			$this->_prepareForPersistanceFunc = null;
		}

		public function setDataModel(A\Model\TableDataModel $dataModel, $setAsCrudModelIfNotSet = true) {
			$this->_dataModel = $dataModel;
			
			if ($setAsCrudModelIfNotSet AND is_null($dataModel->getCrudModel())) {
				$dataModel->setCrudModel($this);
			}
		}
		
		/**
		 * 
		 * @return \Aspic\Model\DataModel
		 */
		public function getDataModel() {
			return $this->_dataModel;
		}
		
		/**
		 * 
		 * @return \Aspic\Model\Row
		 */
		public function getRowInstance() {
			return $this->getDataModel()->getRowInstance();
		}

		public function getTableName() {
			return $this->getDataModel()->getTableName();
		}

		/**
		* Shortcut to $this->util()->escape()
		*/
		public function escape($string) {
			return $this->util()->escape($string);
		}

		/**
		* Shortcut to the SqlUtil object
		*/
		public function util() {
			return $this->_db->getSqlUtil();
		}

		/**
		* Same as Util::columnsPrefixer with the table columns
		*/
		public function getPrefixedColumns($tableAlias, $aliasPrefix = null, $aliasSuffix = '') {
			$str = $this->util()->columnsPrefixer($this->getColumnsNames(), $tableAlias, $aliasPrefix, $aliasSuffix);
			
			return $str;
		}

		/**
		* Return a pre-configured QueryBuilder ("from" clause is already set)
		*
		* @param string $tableAlias The alias for the table
		*/
		public function createQueryBuilder($tableAlias = null, $distinct = false) {
			$queryBuilderReflection = new \ReflectionClass(A\Util::getNamespaceName($this).'\QueryBuilder');
			$obj = $queryBuilderReflection->newInstanceArgs(array($this->util(), $this->_db));

			$obj->from('`'.$this->getTableName().'`'.(($tableAlias)?' '.$tableAlias:''), '*', $distinct);

			return $obj;
		}

		/**
		 * Return the columns names from the table data model
		 * @return array
		 */
		public function getColumnsNames() {
			return A\UArray::getLinesIndex($this->_dataModel->getCols(), 'name');
		}

		/**
		* Return an array containing for each $value : column = "value" if column is a column of the table
		*/
		protected function getAssociationPairs($values, $isWhereExpression, array $params = array()) {
			return $this->util()->getAssociationPairs($values, $isWhereExpression, array_merge(array('columns' => $this->getColumnsNames()), $params));
		}

		/**
		* Check if field (or an array fields) is a real field of the table
		*
		* @param string|array Field name of field array to check existence
		*/
//		protected function isTableField($fields) {
//			return $this->util()->isTableField($fields, $this->getColumnsNames());
//		}

		/**
		* Insert object in the table
		*/
		public function insert($row) {
			$this->prepareForPersistance($row);
			
			$row = $this->util()->toArray($row);

			$row = $this->util()->getEffectiveFields($row, $this->getColumnsNames());

			$sql = 'INSERT INTO `'.$this->getTableName().'` ('.implode(',', array_keys($row)).') VALUES(';

			$sql .= implode(',', array_map(array($this->util(), 'escapeAndSurround'), $row));

			$sql .= ')';
			
			return $this->_db->query($sql);
		}
		
		public function addRow($row) {
			$this->insert($row);
			
			return $this->_db->getLastInsertedId();
		}

		/**
		* Retreive all rows of the table
		*/
		public function fetchAll($orderBy = null, $limit = null, array $params = array()) {
			return $this->fetch(array(), '*', $orderBy, $limit, $params);
		}

		/**
		* Retreive some rows of the table that match the where parameter
		*/
		public function fetch($where, $cols = '*', $orderBy = null, $limit = null, array $params = array()) {
			$queryBuilder = $this->createQueryBuilder(null, (bool)A\Util::getIfSet($params['distinct']));

			$queryBuilder
			->columns($cols)
			->where($where, $params)
			;

			$queryBuilder
			->orderBy($orderBy)
			->limit($limit)
			;

			$res = $this->getResults($queryBuilder->sql());

			return $res;
		}

		/**
		* Same as fetch excepts that field values that are empty string '' or null (strict, 0 will pass) will not be treated
		* This is usefull for user search, when parameters could be ommited
		*/
		public function fetchSearch($where, $cols = '*', $orderBy = null, $limit = null, array $searchFieldsConstraint = array(), array $params = array()) {
			$whereParams = A\UArray::mergeRecursiveSimple($params, array(
				'search' => true,
				'searchFieldsConstraint' => ((count($searchFieldsConstraint) > 0)?$searchFieldsConstraint:array('*' => Util::NOT_NULL | Util::NOT_EMPTY)),
			));

			return $this->fetch($where, $cols, $orderBy, $limit, $whereParams);
		}

		/**
		* Same as fetch but with a left join clause
		*
		* @param array $joins Array containing the joins to make like:
		* array(array('table2 t2', 't1.id = t2.table1Id')) Or:
		* array('table2 t2', 't1.id = t2.table1Id') when only one join
		*/
		public function fetchJoin($tableAlias, $joins, $where = null, $cols = '*', $orderBy = null, $limit = null, array $params = array()) {
			$queryBuilder = $this->createQueryBuilder($tableAlias, (bool)A\Util::getIfSet($params['distinct']));

			$queryBuilder->columns($cols);

			if($where !== null) {
				$queryBuilder->where($where, $params);
			}

			if(!is_array($joins[0])) {
				$joins = array($joins);
			}

			foreach($joins as $j) {
				$queryBuilder->joinLeft($j[0], $j[1]);
			}

			$queryBuilder
			->orderBy($orderBy)
			->limit($limit)
			;

			$res = $this->getResults($queryBuilder->sql());

			return $res;
		}

		/**
		* Retreive the first row of the table that match the where parameter
		*/
		public function fetchOne($where, $cols = '*', $orderBy = null, $limit = null, array $params = array()) {
			$list = $this->fetch($where, $cols, $orderBy, $limit, $params);

			if(count($list))
				return $list[0];
			else
				return null;
		}
		
		/**
		 * Fetch data and make a row object recursively with joins
		 * @param array|string $joins A list (array) or an unique string of joins to retrieve 
		 * strings should represent the foreign key object name (see DataModel) separate by a point "." for further foreign keys
		 * ex: array('topic.forum') to fetch a forum post with its topic and forum
		 * or array('topic:topicTableAlias.forum:forumTableAlias') to define alias that will be used them in where condition
		 * 
		 * Using something like array('!topics.!post') on a forum table will return forum(s) topics and for each of them all their posts
		 * This method may slow down performance when used intensively, so you should cache data returned for further usage.
		 */
		public function fetchTree($mainTableAlias = null, $joins, $where = array(), $orderBy = null, $limit = null, array $params = array()) {
			if ($mainTableAlias == null) {
				$mainTableAlias = $this->getTableName();
			}
			
			// Checking given joins strings
			
			if (!is_array($joins)) {
				$joins = array($joins);
			}
			
			$joins = array_unique($joins);
			
			for ($i = 0; $i < count($joins); $i++) {
				
				for ($j = $i + 1; $j < count($joins); $j++) {
					if(preg_match('#'.preg_quote($joins[$i]).'#', $joins[$j])) {
						throw new A\Exception('Incorrect join "'.$join[$i].'" because found in "'.$joins[$j].'"');
					}
				}
				
				$joins[$i] = explode('.', $joins[$i]);
			}
			
			// End of checking
			
			##### GENERATING SQL #####
			
			$queryBuilder = $this->createQueryBuilder($mainTableAlias);
			$queryBuilder->addColumns($this->getPrefixedColumns($mainTableAlias, ''));
			
			$tableAliasIndex = 1;
			
			foreach ($joins as $jParts) {
				$currentModel = $this->getDataModel();
				$currentColAliasPrefix = '';
				$parentTableAlias = $mainTableAlias;
				
				// Looping until reached deepest join
				foreach ($jParts as $joinPropName) {
					if (strpos($joinPropName, ':')) {
						list($joinPropName, $tableAlias) = explode(':', $joinPropName);
					}
					
					#### Looking for a one-to-many relation ####
					// Checking for a "!" in front of join's property's name which means to retrieve
					//  many data objects with foreign key pointing on current table
					// We have to search into all DataModel created to look for a DataModel with oneToManyName
					// equal to $joinPropName and see if there effectively a foreign key to current table into 
					// that dataModel
					
					$isOppositeFk = false;
					
					if (A\UString::beginsWith($joinPropName, '!', $nextStr)) {
						$joinPropName = $nextStr;
						
						$isOppositeFk = true;
						
						$ofk = $currentModel->getOppositeForeignKeyByPropertyName($joinPropName);
						
						if(is_null($ofk)) {
							throw new A\Exception('No Foreign key with object name "'.$joinPropName.'" references current DataModel');
						}
						
						$foreignModel = $ofk['dataModel'];
					}
					
					#### End looking for a one-to-many relation ####
					
					else {
					
						$fk = $currentModel->getForeignKeyByPropertyName($joinPropName);

						if(is_null($fk)) {
							throw new A\Exception('Foreign key with object name "'.$joinPropName.'" does not exists');
						}

						$fkCol = $currentModel->getCol($fk['colName']);
						$foreignModel = $fk['dataModel'];

					}
					
					if(!isset($tableAlias)) {
						$tableAlias = $foreignModel->getTableName().'_'.$tableAliasIndex;
					}

					$currentColAliasPrefix = A\UString::appendOrEqual($currentColAliasPrefix, $joinPropName, '_');

					$queryBuilder->addColumns($this->util()->columnsPrefixer($foreignModel->getColsNames(), $tableAlias, $currentColAliasPrefix.'_'));
					
					if ($isOppositeFk) {
						$onData = array($parentTableAlias.'.'.$currentModel->getPrimaryKeyName() => $tableAlias.'.'.$ofk['colName']);
					}
					else {
						$onData = array($parentTableAlias.'.'.$fk['colName'] => $tableAlias.'.'.$fk['dataModel']->getPrimaryKeyName());
					}
					
					$queryBuilder->joinLeft($foreignModel->getTableName().' '.$tableAlias, $onData);
					
					$currentModel = $foreignModel;
					
					$tableAliasIndex++;

					$parentTableAlias = $tableAlias;
					unset($tableAlias);
					
				}
			}
			
			$queryBuilder
			->where($where, $params)
			->orderBy($orderBy)
			->limit($limit)
			;
			
			##### END OF GENERATING SQL #####
		
			$rawRes = $this->getResults($queryBuilder->sql());
			
			if($rawRes) {
				##### CREATING OBJECTS #####
				//
				// An array like array('tableName' => array(...Table objects...), ..)
				$objs = array();

				foreach ($joins as $jParts) {
					$currentModel = $this->getDataModel();
					$currentColAliasPrefix = '';

					foreach ($jParts as $joinPropName) {
						if (strpos($joinPropName, ':')) {
							list($joinPropName, $tableAlias) = explode(':', $joinPropName);
						}

						$isOppositeFk = false;

						if (A\UString::beginsWith($joinPropName, '!', $nextStr)) {
							$joinPropName = $nextStr;

							$isOppositeFk = true;
						}

						if($isOppositeFk) {
							$ofk = $currentModel->getOppositeForeignKeyByPropertyName($joinPropName);

							$foreignModel = $ofk['dataModel'];
						}
						else {
							$fk = $currentModel->getForeignKeyByPropertyName($joinPropName);

							$foreignModel = $fk['dataModel'];
						}

						$currentColAliasPrefix = A\UString::appendOrEqual($currentColAliasPrefix, $joinPropName, '_');
						
						$this->loopFetchTreeResult($rawRes, $currentColAliasPrefix, $foreignModel, $objs);

						$currentModel = $foreignModel;

					}
				}
				
				// Fill fulling main table data with col not in $sqlAliasTreated
				
				$this->loopFetchTreeResult($rawRes, '', $this->getDataModel(), $objs);
				
				##### END OF CREATING OBJECTS #####
				
				##### LINKING OBJECTS #####
				
				foreach ($joins as $jParts) {
					$currentModel = $this->getDataModel();
					$currentColAliasPrefix = '';

					foreach ($jParts as $joinPropName) {
						if (strpos($joinPropName, ':')) {
							list($joinPropName, $tableAlias) = explode(':', $joinPropName);
						}

						$isOppositeFk = false;

						if (A\UString::beginsWith($joinPropName, '!', $nextStr)) {
							$joinPropName = $nextStr;

							$isOppositeFk = true;
						}
						
						if($isOppositeFk) {
							$ofk = $currentModel->getOppositeForeignKeyByPropertyName($joinPropName);

							$foreignModel = $ofk['dataModel'];
						}
						else {
							$fk = $currentModel->getForeignKeyByPropertyName($joinPropName);

							$foreignModel = $fk['dataModel'];
						}
						
						$objTableName = $foreignModel->getTableName();
						
						if($isOppositeFk) {
							// Then data will be an array
							
							$foreignKeyName = $ofk['colName'];
							
							foreach($objs[$objTableName] as $tableObj) {
								$data = array();
								
								$foreignKeyValue = $tableObj->{$ofk['colName']};
								$parentTableName = $currentModel->getTableName();
								
								foreach($objs[$parentTableName] as $parentTableObj) {
									if ($parentTableObj->getPrimaryKeyValue() == $foreignKeyValue) {
										$objPos = &$parentTableObj->get($joinPropName);
										$objPos[] = $tableObj;
									}
								}
							}
						}
						else {
							// Then data will be a single object
							
							$foreignKeyName = $fk['colName'];
							
							foreach($objs[$objTableName] as $tableObj) {
								$primaryKeyValue = $tableObj->getPrimaryKeyValue();
								$parentTableName = $currentModel->getTableName();
								
								foreach($objs[$parentTableName] as $parentTableObj) {
									if ($parentTableObj->{$fk['colName']} == $primaryKeyValue) {
										$objPos = &$parentTableObj->get($joinPropName);
										$objPos = $tableObj;
										
										break;
									}
								}
							}
						}
						
						$currentModel = $foreignModel;
					}
				}
				
				##### END LINKING OBJECTS #####
				
				$res = $objs[$this->getTableName()];

			}
			else {
				$res = null;
			}
			
			return $res;
			
		}
		
		/**
		 * Create objects (if not already exist) for all rows from the db result set
		 * @param type $res
		 * @param type $currentColAliasPrefix
		 * @param type $foreignModel
		 * @param type $objs
		 */
		protected function loopFetchTreeResult($res, $currentColAliasPrefix, $foreignModel, &$objs) {
			foreach ($res as $rawRow) {
				$foreignTableName = $foreignModel->getTableName();
				$sqlPrimaryKeyAlias = A\UString::appendOrEqual($currentColAliasPrefix, $foreignModel->getPrimaryKeyName(), '_');
//				var_dump($sqlPrimaryKeyAlias);
				// Looking in objects array if given object already exist
				if (
					!isset($objs[$foreignTableName]) OR
					A\UArray::inLinesSearch($objs[$foreignTableName], 
						$rawRow->{$sqlPrimaryKeyAlias}, 
						$foreignModel->getPrimaryKeyName(), 
						true
					)
					 === false
				) {
					$obj = $foreignModel->getRowInstance();
//					$obj = new \stdclass;

					foreach ($foreignModel->getCols() as $col) {
						$alias = A\UString::appendOrEqual($currentColAliasPrefix, $col['name'], '_');

						$obj->{$col['name']} = $rawRow->{$alias};
					}

					$objs[$foreignTableName][] = $obj;
				}

			}
		}
		
		/**
		 * Same as fetchTree but return only one row
		 */
		public function fetchRowTree($where, $orderBy = null, $limit = null, array $params = array()) {
			$rows = $this->fetchTree($joins, $where, $orderBy, $limit);
			
			if(A\UArray::countArray($rows)) {
				return $rows[0];
			}
			else {
				return null;
			}
		}

		/**
		* Update rows that match the where parameter
		*/
		public function update($updateData, array $where = array()) {
			$updateData = $this->util()->toArray($updateData);
			
			$sql = 'UPDATE `'.$this->getTableName().'` SET ';
			$sql .= implode(', ', $this->getAssociationPairs($updateData, false));

			$sql .= ' '.$this->util()->getWhereString($where);

			return $this->_db->query($sql);
		}

		/**
		* Update a row (using primary key field)
		 * @param string $field The field to use for retrieving $row in DB (default: primary key)
		 *
		 * /!\ This method is also used for CrudModel interface, so params could not change without making big change
		 * @todo Implement recursive update manually or automatically, depending on Foreign keys CASCADE value (SET NULL, NO ACTION, ..)
		*/
		public function updateRow($row, $recursive = true, $field = null) {
			$this->prepareForPersistance($row);
			
			$row = $this->util()->toArray($row);
			
			if($field == null) {
				$field = $this->_dataModel->getPrimaryKeyName();
			}
			elseif(!$this->_dataModel->colExists($field)) {
				throw new A\Exception($field.' is does not exit in table columns');
			}

			if(!isset($row[$field])) {
				throw new A\Exception('Field "'.$field.'" must be set to be used into where condition of update request');
			}

			$rowData = $row;
			unset($rowData[$field]);
			
			return $this->update($rowData, array($field => $row[$field]));
		}

		/**
		* Delete rows that match the where parameter
		*/
		public function delete($where) {
			$sql = 'DELETE FROM `'.$this->getTableName().'`';

			$sql .= ' '.$this->util()->getWhereString($where);

			return $this->_db->query($sql);
		}

		/**
		* Delete all rows
		*/
		public function deleteAll() {
			$sql = 'DELETE FROM `'.$this->getTableName().'`';

			return $this->_db->query($sql);
		}

		/**
		* Delete a row (using primary key field or $field if not null)
		 * @param boolean $recursive Delete also rows that have a foreign key 
		 * to the given row ((opposite)foreign keys have to be set in linked DataModel)
		*/
		public function deleteRow($primaryKeyValue, $recursive = false, $field = null) {
			if($field == null) {
				$field = $this->_dataModel->getPrimaryKeyName();
			}
			elseif(!$this->_dataModel->colExists($field)) {
				throw new Exception(htmlentities($field).' is not an existing field');
			}
			
			if ($recursive) {
				foreach($this->getDataModel()->getOppositeForeignKeys() as $ofk) {
					$ofkDataModel = $ofk['dataModel'];

					// Find all rows of the linked model that have their foreign key pointing on me
					$ofkRows = $ofkDataModel->getCrudModel()->fetchRows($ofk['colName'], $primaryKeyValue);

					foreach($ofkRows as $ofkRow) {
						$ofkRow->delete(true);
					}

				}	

				$ofkDataModel->getCrudModel()->deleteRows($ofk['colName'], $ofkRow->getPrimaryKeyValue());
			}

			$sql = 'DELETE FROM `'.$this->getTableName().'`';
			$sql .= ' WHERE '.$this->util()->getAssociationPairString($field, '=', $primaryKeyValue, true);

			return $this->_db->query($sql);
		}

		/**
		* Return a row from a primary key value (if $field = null)
		*/
		public function find($keyValue, $field = null, $cols = '*') {
			if($field == null) {
				$field = $this->_dataModel->getPrimaryKeyName();
			}
			elseif(!$this->_dataModel->colExists($field)) {
				throw new Exception($field.' is does not exit in table columns');
			}

			$sql = 'SELECT '.$cols.' FROM `'.$this->getTableName().'` WHERE '.$this->util()->getAssociationPairString($field, '=', $keyValue, true);

			return $this->getRow($sql);
		}
		
		public function readRow($pkValue) {
			return $this->find($pkValue);
		}

		/**
		* Return the number of rows that match the where parameter
		*/
		public function count($where = array(), array $params = array()) {
			$sql = 'SELECT COUNT(*) FROM `'.$this->getTableName().'`';

			$sql .= ' '.$this->util()->getWhereString($where, true, $params);

			return $this->_db->getVar($sql);
		}

		public function exists($where = array(), array $params = array()) {
			return ($this->count($where, $params) > 0);
		}

		/**
		* Search the database with whereStr and params (optionnal).
		* This function parse whereStr with SqlUtil::quoteInto wich use UString::fromArgs()
		* so passed args are automatically escaped
		* ex:
		* $email = 'example@example.com'
		* $id = 25;
		* where('email = ? AND id != ?', $email, $id)
		* => SELECT * FROM tableName WHERE email = "example@example.com" AND id != 25
		*/
		public function where($whereStr) { // , params1, param2
			$params = array();

			if(count(func_get_args()) > 1) { // Is there params ?
				$params = array_slice(func_get_args(), 1);
			}

			$args = array(
				'*',
				$whereStr,
			);

			$args = array_merge($args, $params);

			return call_user_func_array(array($this, 'whereCols'), $args);
		}

		/**
		* Same as where() but with columns selection
		*/
		public function whereCols($cols, $whereStr) { // , params1, param2
			if(count(func_get_args()) > 2) { // Is there params ?
				$params = array_slice(func_get_args(), 2);

				$whereStr = $this->util()->quoteIntoArray($whereStr, $params);
			}

			$sql = 'SELECT '.$cols.' FROM '.$this->getTableName().' WHERE '.$whereStr;

			return $this->getResults($sql);
		}
		
		protected function getRowClass() {
			if ($this->_dataModel->getRowClass()) {
				$rowClass = $this->_dataModel->getRowClass();
			}
			else {
				$rowClass = static::ROW_CLASS;
			}
			
			return $rowClass;
		}

		/**
		* Return an array with the result of the sql instruction and build each row of the result into the class property "_rowClass"
		*/
		public function getResults($sql) {
			$params = array($this->getRowClass());
			
			if ($this->getDataModel()->rowClassTakeDataModel()) {
				$params[] = $this->getDataModel();
			}
			
			$results = $this->_db->getResults($sql, AbstractDatabase::FETCH_TYPE_CLASS, $params);
			
			return $results;
		}

		/**
		* Return an array with the result of the sql instruction and build each row of the result into the class property "_rowClass"
		*/
		public function getRow($sql) {
			$params = array($this->getRowClass());
			
			if ($this->getDataModel()->rowClassTakeDataModel()) {
				$params[] = $this->getDataModel();
			}
			
			
			$result = $this->_db->getRow($sql, AbstractDatabase::FETCH_TYPE_CLASS, $params);

			return $result;
		}

		### METHODS THAT NEED A DataModel ###

		public function createTable() {
			if(is_null($this->getDataModel())) {
				throw new A\Exception('Method "'.__METHOD__.'" could not be called because no DataModel has been set');
			}

			$arrLines = array();
			$primaryKeysCols = array();

			foreach ($this->_dataModel->getCols() as $name => $col) {
				$line = $this->util()->getColDefinition($col);
				
				$line .= ', ';
				
				$arrLines[] = $line;
			}
			
			$pkStr = '';
			
			if ($this->getDataModel->hasPrimaryKey()) {
				$pkStr = 'CONSTRAINT `'.$this->getTablePrimaryKeyConstraintName().
						'` PRIMARY KEY (`'.$this->getDataModel()->getPrimaryKeyName().'`)';
			}
			
			$constraintsStrs = array();
			
			foreach($this->getDataModel()->getForeignKeys() as $fk) {
					$fkStr = 'CONSTRAINT `'.
							$this->getColForeignKeyConstraintName($fk['dataModel']->getTableName()).
							'` FOREIGN KEY (`'.$fk['colName'].'`) REFERENCES '.
							$fk['dataModel']->getTableName().
							'(`'.$fk['model']->getPrimaryKeyName().'`)';

					$constraintsStrs[] = $fkStr;
			}
			
			
			$cbSql = new A\CodeBuilder;
			$cbSql->next('CREATE TABLE `'.$this->getTableName().'` (');
			$cbSql->indent();
				$cbSql->nl(implode("\n", $arrLines));
				$cbSql->nl(implode("\n", $constraintsStrs));
				$cbSql->nl($pkStr);
			$cbSql->revIndent();
			$cbSql->nl(')');

//			var_dump($cbSql->out());
			
			$this->_db->query($cbSql->out());
			
		}
		
		/**
		 * Return the primary key constraint name for the table name
		 */
		protected function getTablePrimaryKeyConstraintName() {
			return $this->getTableName().'_PK';
		}
		
		/**
		 * Return the foreign key constraint name for a given column
		 * @param type $col 
		 */
		protected function getColForeignKeyConstraintName($foreignTableName) {
			static $i = 1;
			
			return $this->getTableName().'_'.$foreignTableName.'_FK'.$i++;
		}

		/**
		 * Currently, only add new columns and remove old columns from table (no update of type)
		 */
		public function updateTable() {
			throw new Aspic\NotImplementedException;
		}

		public function dropTable() {
			if(is_null($this->getDataModel())) {
				throw new A\Exception('Method "'.__METHOD__.'" could not be called because no DataModel has been set');
			}
			
			$sql = 'DROP TABLE `'.$this->getTableName().'`';
			
			return $this->_db->query($sql);
		}
		
		/**
		 * Return the columns of the table from a SQL request to DB
		 */
		public function fetchTableColumns() {
			return $this->_db->fetchTableColumns($this->getTableName());
		}
		
		public function setPrepareForPersistanceFunc($callback) {
			if (!is_callable($callback)) {
				throw new A\Exception('Callback is not valid');
			}
			
			$this->_prepareForPersistanceFunc = $callback;
		}
		
		/**
		* This method is called before adding or updating an object representing a table line
		* @param object $obj The object containing data that should be prepare for db
		*/
		protected function prepareForPersistance($obj) {
			if ($this->_prepareForPersistanceFunc) {
				call_user_func($this->_prepareForPersistanceFunc, $obj);
			}
		}
		
		public function fetchRows($colName, $colValue) {
			return $this->fetch(array($colName => $colValue));
		}
		
		public function deleteRows($colName, $colValue) {
			return $this->delete(array($colName => $colValue));
		}

	}

?>